package com.wondertek.service.service.sys.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wondertek.common.enums.StatusEnum;
import com.wondertek.common.exception.AppException;
import com.wondertek.common.util.security.LoginUser;
import com.wondertek.service.dto.sys.req.SysMenuPoReq;
import com.wondertek.service.dto.sys.res.SysMenuTypeRes;
import com.wondertek.service.entity.sys.SysMenuPo;
import com.wondertek.service.mapper.sys.SysMenuMapper;
import com.wondertek.service.service.sys.ISysMenuService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.compress.utils.Lists;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author xqs
 */
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenuPo>
    implements ISysMenuService {

    private static final String LOGIN_USER_KEY = "iot:dh:sys:login:user:";
    private static final String LOGIN_TOKEN_KEY = "iot:dh:sys:login:token:";

    private final SysMenuMapper sysMenuMapper;

    private final RedisTemplate<String,Object> redisTemplate;

    @Override
    public List<SysMenuPo> findListByUserId(String userId) {
        return sysMenuMapper.findListByUserId(userId);
    }

    @Override
    public List<SysMenuPo> findListPermissionByUserId(String userId) {
        List<SysMenuPo> sysMenuPoList = Lists.newArrayList();
        sysMenuPoList.addAll(sysMenuMapper.findListPermissionByUserId(userId));
        sysMenuPoList.addAll(sysMenuMapper.findCommonPermission());
        return sysMenuPoList;
    }

    @Override
    public List<SysMenuPo> findListButtonPermission() {
        List<SysMenuPo> list = sysMenuMapper.findListButtonPermission();
        return list;
    }

    @Override
    public List<SysMenuPo> findTreeByUserId(String userId) {
        List<SysMenuPo> sysMenuPos = toTree(findListByUserId(userId), "0");
        List<SysMenuPo> buttonMenus = sysMenuMapper.findListPermissionByUserId(userId);
        return toButtonTree(sysMenuPos, buttonMenus);
    }


    @Override
    public List<SysMenuPo> findList() {
        return this.lambdaQuery()
            .orderByDesc(SysMenuPo::getSort).orderByDesc(SysMenuPo::getCreateTime)
            .eq(SysMenuPo::getStatus, StatusEnum.ENABLE.getCode())
            .eq(SysMenuPo::getMenuType, "MENU").list();
    }

    @Override
    public List<SysMenuPo> findTree() {
        return toTree(findList(), "0");
    }

    @Override
    public List<SysMenuTypeRes> findListByRoleIdType(String roleId) {
        List<SysMenuTypeRes> sysMenuPos = sysMenuMapper.findListByRoleIdType(roleId);
        return sysMenuPos;
    }

    @Override
    public List<SysMenuPo> findListByRoleId(String roleId) {
        List<SysMenuPo> sysMenuPos = sysMenuMapper.findListByRoleId(roleId);
        return sysMenuPos;
    }

    @Override
    public List<SysMenuPo> findTreeByRoleId(String roleId) {
        return toTree(findListByRoleId(roleId), "0");
    }

    @Override
    public void add(SysMenuPoReq sysMenuPoReq) {
        //保存
        switch (sysMenuPoReq.getMenuType()) {
            case "MENU":
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getMenuName, sysMenuPoReq.getMenuName()).count() > 0) {
                    throw new AppException("menu.name.repeat");
                }
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).count() > 0) {
                    throw new AppException("authority.code.repeat");
                }
                break;
            case "BUTTON":
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getParentId, sysMenuPoReq.getParentId()).eq(SysMenuPo::getMenuName, sysMenuPoReq.getMenuName()).count() > 0) {
                    throw new AppException("button.name.authority.repeat");
                }
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getParentId, sysMenuPoReq.getParentId()).eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).count() > 0) {
                    throw new AppException("button.code.authority.repeat");
                }
                break;
        }
        SysMenuPo sysMenuPo = new SysMenuPo();
        BeanUtil.copyProperties(sysMenuPoReq, sysMenuPo);
        String sysMenuId = IdUtil.fastSimpleUUID();
        sysMenuPo.setId(sysMenuId);
        this.save(sysMenuPo);
    }

    @Override
    public void update(SysMenuPoReq sysMenuPoReq) {
        //更新
        switch (sysMenuPoReq.getMenuType()) {
            case "MENU":
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getMenuName, sysMenuPoReq.getMenuName()).count() > 0) {
                    throw new AppException("menu.name.repeat");
                }
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).count() > 0) {
                    throw new AppException("authority.code.repeat");
                }
                break;
            case "BUTTON":
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getParentId, sysMenuPoReq.getParentId()).eq(SysMenuPo::getMenuName, sysMenuPoReq.getMenuName()).count() > 0) {
                    throw new AppException("button.name.authority.repeat");
                }
                if (this.lambdaQuery().eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).eq(SysMenuPo::getParentId, sysMenuPoReq.getParentId()).eq(SysMenuPo::getMenuCode, sysMenuPoReq.getMenuCode()).count() > 0) {
                    throw new AppException("button.code.authority.repeat");
                }
                break;
        }
        SysMenuPo sysMenuPo = new SysMenuPo();
        BeanUtil.copyProperties(sysMenuPoReq, sysMenuPo);
        this.updateById(sysMenuPo);
    }

    @Override
    public void delete(String id) {

    }

    @Override
    public List<SysMenuPo> getChilrenList(String id, String menuType) {
        return sysMenuMapper.getChilrenList(id, menuType);
    }

    @Override
    public List<SysMenuPo> findAllTree() {
        return toTree(this.lambdaQuery()
            .orderByDesc(SysMenuPo::getSort).orderByDesc(SysMenuPo::getCreateTime)
            .eq(SysMenuPo::getStatus, StatusEnum.ENABLE.getCode())
            .list(), "0");
    }

    private List<SysMenuPo> toTree(List<SysMenuPo> listData, String parentId) {
        List<SysMenuPo> treeData = new ArrayList<>();
        Iterator<SysMenuPo> it = listData.iterator();
        while (it.hasNext()) {
            SysMenuPo item = it.next();
            if (item.getParentId().equals(parentId)) {
                SysMenuPo sysMenuPo = new SysMenuPo();
                BeanUtil.copyProperties(item, sysMenuPo);
                treeData.add(sysMenuPo);
                //把已经添加了的数据删除，减少迭代次数
                it.remove();
            }
        }
        for (SysMenuPo item : treeData) {
            item.setChildren(toTree(listData, item.getId()));
        }
        return treeData.size() == 0 ? new ArrayList<>() : treeData;
    }


    private List<SysMenuPo> toButtonTree(List<SysMenuPo> sysMenuPos, List<SysMenuPo> buttonMenus) {
        for (SysMenuPo sysMenuPo : sysMenuPos
        ) {
            Iterator<SysMenuPo> it = buttonMenus.iterator();
            while (it.hasNext()) {
                SysMenuPo item = it.next();
                if (item.getParentId().equals(sysMenuPo.getId())) {
                    if (sysMenuPo.getAuths() != null && !sysMenuPo.getAuths().isEmpty()) {
                        sysMenuPo.getAuths().add(item.getMenuCode());
                    } else {
                        List<String> list = new ArrayList<>();
                        list.add(item.getMenuCode());
                        sysMenuPo.setAuths(list);
                    }
                    //把已经添加了的数据删除，减少迭代次数
                    it.remove();
                }
            }
            if (sysMenuPo.getChildren() != null && !sysMenuPo.getChildren().isEmpty()) {
                toButtonTree(sysMenuPo.getChildren(), buttonMenus);
            }
        }
        return sysMenuPos.size() == 0 ? new ArrayList<>() : sysMenuPos;
    }

    @Async("threadPoolTaskExecutor")
    @Override
    public void updateUserPermissions(String userId) {
        // 从缓存中获取登录用户
        String token = (String) redisTemplate.opsForValue().get(LOGIN_USER_KEY + userId);
        if (StrUtil.isNotBlank(token)) {
            LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get(LOGIN_TOKEN_KEY + token);
            if (null != loginUser) {
                // 重新获取最新的菜单和按钮权限
                List<String> authoritiesMenu = this.findListByUserId(userId).stream()
                        .map(SysMenuPo::getMenuCode)
                        .filter(Objects::nonNull)
                        .toList();
                //查询用户拥有的按钮权限code
                List<String> authoritiesButton = this.findListPermissionByUserId(userId).stream()
                        .map(SysMenuPo::getMenuCode)
                        .filter(Objects::nonNull)
                        .toList();
                // 更新登录用户对象中的权限信息
                loginUser.setAuthoritiesMenu(new HashSet<>(authoritiesMenu));
                loginUser.setAuthoritiesButton(new HashSet<>(authoritiesButton));
                // 更新缓存中的LoginUser对象
                // 获取原有的过期时间
                Long expire = redisTemplate.getExpire(LOGIN_TOKEN_KEY + token);
                redisTemplate.opsForValue().set(LOGIN_TOKEN_KEY + token, loginUser);

                // 重新设置原有的过期时间
                if (expire != null && expire > 0) {
                    redisTemplate.expire(LOGIN_TOKEN_KEY + token, expire, TimeUnit.SECONDS);
                }
            }
        }
    }

}
